package edu.psu.collegerecommendationhelper.algorithm;

import java.io.File;

import weka.classifiers.Evaluation;
import weka.classifiers.lazy.IBk;
import weka.core.Instances;
import weka.core.converters.ArffLoader;
import weka.core.neighboursearch.NearestNeighbourSearch;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.RemoveUseless;
import edu.psu.collegerecommendationhelper.dataentry.WekaDBConnect;


/**
 * 
 * @author cbarone
 *
 *This file implements the Weka code to build a model, save and load models,
 *load training data, and evaluate the data.
 */
public class WekaToolUtility {
//public class WekaToolUtility implements Runnable{

	private static IBk lazyLearner = null;
	private static FileLocator fl = null;
	private WekaDBConnect db = null;
	
	
	//Class constructor.
	public WekaToolUtility() {
		lazyLearner = new IBk();
		fl = new FileLocator();
		db = new WekaDBConnect();
	}
	
	
	//This function will return to the caller a nearest neighbor 
	//search algorithm. The algorithm is generated from a learner model.
	public NearestNeighbourSearch generateModelAlgorithm () {		
		lazyLearner = buildLearnerModel();
			
		return lazyLearner.getNearestNeighbourSearchAlgorithm();
	}
	
	
	//This function gets the training data and builds the classifier model.
	private IBk buildLearnerModel() {	    
		boolean foundModel = loadLearnermodel();
		
		if (!foundModel)
		{
			Instances data = db.retrieveTrainingData();
			
			data.setClassIndex(0);		
			lazyLearner.setKNN(20);
			
			// train lazy learner training data data
			try {
				lazyLearner.buildClassifier(data);
			} catch (Exception e) {
				e.printStackTrace();
			}

			boolean rslt = saveLearnerModel(lazyLearner);
		}
		 
	    return lazyLearner;
	}

	
	//This function loads a previously saved Learner model from model location.
	private boolean loadLearnermodel() {
		
		try {
			lazyLearner = (IBk) weka.core.SerializationHelper.read(fl.getModelLocation());
			
			return true;
		} catch (Exception e) {
			return false;
		}
		 
	}

	
	//This function evaluates test data on given model.
	public boolean evaluateTestData(Instances testData) throws Exception {
		boolean foundModel = loadLearnermodel();
		Boolean rslt = false;
		
		if (foundModel) {
			Instances trainingSet = retrieveTrainingDataFromFile();
			trainingSet.setClassIndex(0);

			Evaluation eval = new Evaluation(trainingSet);
			eval.evaluateModel(lazyLearner, testData);

			rslt = saveLearnerModel(lazyLearner);
		}		
		
		return rslt;
	}

		
	//This function saves a model to the model location for reuse.
	private boolean saveLearnerModel(IBk lazyLearnerModel) {		
		try {
			weka.core.SerializationHelper.write(fl.getModelLocation(), lazyLearnerModel);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}


	//This function returns filtered data to train model with from the arff location.
	private Instances retrieveTrainingDataFromFile() {
	    Instances dataFiltered = null;
	    
	    try {
			ArffLoader loader = new ArffLoader();
			
			loader.setFile(new File(fl.getArffLocation()));
			Instances raw = loader.getDataSet();
			
			RemoveUseless nfilter = new RemoveUseless();
			nfilter.setInputFormat(raw);
			
			dataFiltered = Filter.useFilter(raw, nfilter);
			
	    } catch (Exception e1) {			
			e1.printStackTrace();
		}
		
		return dataFiltered;
	}
	

	
/*	
	public static void main(String[] args) {

	       (new Thread(new WekaToolUtility() )).start();
		}

		@Override
		public void run() {

			double[] coeffs = retrieveModelCoefficients();
			
			for (double d : coeffs)
			  System.out.println(d);
			
		}
*/
}
